﻿using System;
using System.Numerics;
using System.Threading.Tasks;
using GKS.Extensions;
using GKS;

namespace GKS.Math.LinearAlgebra {
    /// <summary>
    /// A class that implements generic Matrices.
    /// </summary>
    /// <remarks>
    /// By default this class manages the .NET framework numerik types:
    ///     - byte
    ///     - decimal
    ///     - double
    ///     - float
    ///     - int
    ///     - long
    ///     - sbyte
    ///     - short
    ///     - uint
    ///     - ulong
    ///     - ushort
    ///     - Complex
    ///     - BigInteger
    /// However, given a custom type with the defined following components:
    ///     - Sum function
    ///     - Multiplication function
    ///     - Subtraction function
    ///     - Sum identity
    ///     - Multiplication identity
    /// it is possible to manage that custom type also.
    /// 
    /// Next development steps:
    ///     - Improve Exception management
    ///         - Add error-safe methods (TryAdd, TryMultiply, etc.)
    ///     - Add logging instructions
    /// </remarks>
    public class Matrix<T> {
        #region variables
        /// <summary>
        /// The inner structure for the elements of the Matrix
        /// </summary>
        private T[,] elements;

        private object thisLock = new object();
        #endregion

        #region properties
        /// <summary>
        /// Get the number of the columns in this matrix.
        /// </summary>
        public int Columns { get; private set; }

        /// <summary>
        /// Get the number of rows in this matrix.
        /// </summary>
        public int Rows { get; private set; }

        /// <summary>
        /// Get or Set the multiplication function for the elements
        /// of this Matrix instance.
        /// </summary>
        /// <remarks>
        /// The function must be:
        ///     <b>f: (T, T) -> T</b>
        /// and must do the equivalent operation of the following:
        ///     <b>f(T1, T2) = T1 * T2</b>
        /// </remarks>
        public Func<T, T, T> Multiplication { get; set; }

        /// <summary>
        /// Get or set the sum function for the elements
        /// of this Matrix instance.
        /// </summary>
        /// <remarks>
        /// The function must be:
        ///     <b>f: (T, T) -> T</b>
        /// and must do the equivalent operation of the following:
        ///     <b>f(T1, T2) = T1 + T2</b>
        /// </remarks>
        public Func<T, T, T> Sum { get; set; }

        /// <summary>
        /// Get or Set the division function for the elements
        /// of this Matrix instance.
        /// </summary>
        /// <remarks>
        /// The function must be:
        ///     <b>f: (T, T) -> T</b>
        /// and must do the equivalent operation of the following:
        ///     <b>f(T1, T2) = T1 / T2</b>
        /// </remarks>
        public Func<T, T, T> Division { get; set; }

        /// <summary>
        /// Get or Set the subtraction function for the elements
        /// of this Matrix instance.
        /// </summary>
        /// <remarks>
        /// The function must be:
        ///     <b>f: (T, T) -> T</b>
        /// and must do the equivalent operation of the following:
        ///     <b>f(T1, T2) = T1 - T2</b>
        /// </remarks>
        public Func<T, T, T> Subtraction { get; set; }

        /// <summary>
        /// Get or Set the GreaterThan function for the elements
        /// of this Matrix instance.
        /// </summary>
        /// <remarks>
        /// The function must be:
        ///     <b>f: (T, T) -> bool</b>
        /// and must do the equivalent operation of the following:
        ///     <b>f(T1, T2) = T1 &gt; T2</b>
        /// </remarks>
        public Func<T, T, bool> GreaterThan { get; set; }

        /// <summary>
        /// Get or Set the SmallerThan function for the elements
        /// of this Matrix instance.
        /// </summary>
        /// <remarks>
        /// The function must be:
        ///     <b>f: (T, T) -> bool</b>
        /// and must do the equivalent operation of the following:
        ///     <b>f(T1, T1) = T1 &st; T2</b>
        /// </remarks>
        public Func<T, T, bool> SmallerThan { get; set; }

        /// <summary>
        /// Get or Set the identity element for the sum
        /// operation of the elements of this Matrix instance.
        /// </summary>
        public T SumIdentity { get; set; }

        /// <summary>
        /// Get or Set the identity element for the multiplication
        /// operation of the elements of this Matrix instance.
        /// </summary>
        public T MultiplicationIdentity { get; set; }

        /// <summary>
        /// Check if this Matrix is a square one or not.
        /// </summary>
        public bool IsSquare {
            get {
                lock (thisLock) {
                    return Rows == Columns;
                }
            }
        }

        /// <summary>
        /// Check asynchronously if this Matrix is a square one or not.
        /// </summary>
        public Task<bool> IsSquareAsync {
            get {
                return Task.Run(() => IsSquare);
            }
        }

        /// <summary>
        /// Check if this Matrix is a symmetric one or not.
        /// </summary>
        public bool IsSymmetric {
            get {
                bool result = IsSquare;

                lock (thisLock) {
                    for (int i = 0; result && i < Rows; i++) {
                        for (int j = i; result && j < Columns; j++) {
                            result = result && (elements[i, j].Equals(elements[j, i]));
                        }
                    }
                }

                return result;
            }
        }

        /// <summary>
        /// Check if this Matrix is a diagonal one or not.
        /// </summary>
        public bool IsDiagonal {
            get {
                bool result = IsSquare;

                lock (thisLock) {
                    for (int i = 0; result && i < Rows; i++) {
                        for (int j = 0; result && j < Columns; j++) {
                            if (i != j) {
                                result = (this[i, j].Equals(SumIdentity == null ? Utilities.SumId<T>() : SumIdentity));
                            }
                        }
                    }
                }

                return result;
            }
        }

        /// <summary>
        /// Check asynchronously if this Matrix is a diagonal one or not.
        /// </summary>
        public Task<bool> IsDiagonalAsync {
            get {
                return Task.Run(() => IsDiagonal);
            }
        }

        /// <summary>
        /// Check if this Matrix is singular or not.
        /// </summary>
        public bool IsSingular {
            get {
                return IsSquare && GetDeterminant().Equals(SumIdentity);
            }
        }

        /// <summary>
        /// Check asynchronously if this Matrix is singular or not.
        /// </summary>
        public Task<bool> IsSingularAsync {
            get {
                return Task.Run(() => IsSingular);
            }
        }

        /// <summary>
        /// Check if this Matrix is weakly diagonally dominant or not.
        /// </summary>
        public bool IsWeaklyDiagonallyDominant {
            get {
                bool result = IsSquare;
                T diagonalElement;
                T sumResult;

                lock (thisLock) {
                    var smallerThanFunc = SmallerThan == null 
                                            ? Utilities.SmallerThan<T>() 
                                            : SmallerThan;

                    for (int i = 0; result && i < Rows; i++) {
                        diagonalElement = this[i, i];
                        sumResult = this[i, 0];

                        for (int j = 1; result && j < Columns; j++) {
                            sumResult = Sum(sumResult, this[i, j]);
                        }

                        result = result && smallerThanFunc(sumResult, diagonalElement);
                    }
                }

                return result;
            }
        }

        /// <summary>
        /// Check asynchronously if this Matrix is weakly diagonally dominant or not.
        /// </summary>
        public Task<bool> IsWeaklyDiagonallyDominantAsync {
            get {
                return Task.Run(() => IsWeaklyDiagonallyDominant);
            }
        }

        /// <summary>
        /// Check if this Matrix is strongly diagonally dominant or not.
        /// </summary>
        public bool IsStronglyDiagonallyDominant {
            get {
                bool result = IsSquare;
                T diagonalElement;
                T sumResult;

                lock (thisLock) {
                    var smallerThanFunc = SmallerThan != null
                                            ? SmallerThan
                                            : Utilities.SmallerThan<T>();

                    for (int i = 0; result && i < Rows; i++) {
                        diagonalElement = this[i, i];
                        sumResult = this[i, 0];

                        for (int j = 1; result && j < Columns; j++) {
                            sumResult = Sum(sumResult, this[i, j]);
                        }

                        result = result && smallerThanFunc(sumResult, diagonalElement);
                    }
                }

                return result;
            }
        }

        /// <summary>
        /// Check asynchronously if this Matrix is strongly diagonally dominant or not.
        /// </summary>
        public Task<bool> IsStronglyDiagonallyDominantAsync {
            get {
                return Task.Run(() => IsStronglyDiagonallyDominant);
            }
        }

        /// <summary>
        /// Check if this Matrix is weakly or strongly diagonally dominant.
        /// </summary>
        public bool IsDiagonallyDominant {
            get {
                return IsStronglyDiagonallyDominant || IsWeaklyDiagonallyDominant;
            }
        }

        /// <summary>
        /// Check asynchronously if this Matrix is weakly or strongly diagonally dominant.
        /// </summary>
        public Task<bool> IsDiagonallyDominantAsync {
            get {
                return Task.Run(() => IsDiagonallyDominant);
            }
        }

        /// <summary>
        /// Get the elements contained in this matrix.
        /// </summary>
        public T[,] Elements {
            get {
                T[,] result = new T[Rows, Columns];

                lock (thisLock) {
                    for (int i = 0; i < Rows; i++) {
                        for (int j = 0; j < Columns; j++) {
                            result[i, j] = this[i, j];
                        }
                    }
                }

                return result;
            }
        }

        /// <summary>
        /// Get or Set the value of the element in the given position.
        /// </summary>
        /// <param name="row">
        /// The 0-based index of the row.
        /// </param>
        /// <param name="column">
        /// The 0-based index of the column.
        /// </param>
        /// <returns>
        /// The element in the specified position.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Exception thrown in case the specified index is out of the allowed range.
        /// </exception>
        public T this[int row, int column] {
            get {
                if (row < 0) {
                    throw new IndexOutOfRangeException(
                        "The index of the row cannot be smaller than 0. The row index is 0-based.");
                }

                if (row >= Rows) {
                    throw new IndexOutOfRangeException(
                        string.Format(
                            "The index of the row cannot be greater than {0}.",
                            (Rows - 1)));
                }

                if (column < 0) {
                    throw new IndexOutOfRangeException(
                        "The index of the column cannot be smaller than 0. The row index is 0-based.");
                }

                if (column >= Columns) {
                    throw new IndexOutOfRangeException(
                        string.Format(
                            "The index of the column cannot be greater than {0}.",
                            (Columns - 1)));
                }

                T result;

                lock (elements) {
                    result = elements[row, column];
                }

                return result;
            }
            set {
                if (row < 0) {
                    throw new IndexOutOfRangeException(
                        "The index of the row cannot be smaller than 0. The row index is 0-based.");
                }

                if (row >= Rows) {
                    throw new IndexOutOfRangeException(
                        string.Format(
                            "The index of the row cannot be greater than {0}.",
                            (Rows - 1)));
                }

                if (column < 0) {
                    throw new IndexOutOfRangeException(
                        "The index of the column cannot be smaller than 0. The row index is 0-based.");
                }

                if (column >= Columns) {
                    throw new IndexOutOfRangeException(
                        string.Format(
                            "The index of the column cannot be greater than {0}.",
                            (Columns - 1)));
                }

                lock (elements) {
                    elements[row, column] = value;
                }
            }
        }
        #endregion

        #region constructors
        /// <summary>
        /// The default constructor of a Matrix.
        /// </summary>
        /// <remarks>
        /// This constructor builds 2x2 Matrix.
        /// </remarks>
        public Matrix() : this(dimension: 2) { }

        /// <summary>
        /// A constructor of a square Matrix instance.
        /// </summary>
        /// <param name="dimension">
        /// The dimension of the square Matrix.
        /// </param>
        public Matrix(int dimension) : this(dimension, dimension) { }

        /// <summary>
        /// A constructor of a generic Matrix instance.
        /// </summary>
        /// <param name="rows">
        /// The number of rows the new instance must have.
        /// </param>
        /// <param name="columns">
        /// The number of columns the new instance must have.
        /// </param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified number of rows and/or columns is smaller than 1.
        /// </exception>
        public Matrix(int rows, int columns) {
            if (rows < 0) {
                throw new ArgumentOutOfRangeException("rows", "The number of rows must be at least 0.");
            }

            if (columns < 0) {
                throw new ArgumentOutOfRangeException("columns", "The number of columns must be at least 0.");
            }


            Rows = rows;
            Columns = columns;
            elements = new T[Rows, Columns];
        }
        #endregion

        #region methods
        #region addition methods
        /// <summary>
        /// A method that sums this Matrix to another.
        /// </summary>
        /// <param name="toAdd">
        /// The Matrix to add to this Matrix instance.
        /// </param>
        /// <returns>
        /// The resulting Matrix of the sum between the two matrices.
        /// </returns>
        /// <remarks>
        /// This method works for the standard .NET numeric types.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the number of rows and columns is different in 
        /// this Matrix and the other Matrix. 
        /// </exception>
        public Matrix<T> Add(Matrix<T> toAdd) {
            return Add(toAdd, Utilities.Sum<T>());
        }

        /// <summary>
        /// A method that sums this Matrix to another.
        /// </summary>
        /// <param name="toAdd">
        /// The Matrix to add to this Matrix instance.
        /// </param>
        /// <param name="sumFunction">
        /// The function used to sum two elements of the matrices.
        /// </param>
        /// <returns>
        /// The resulting Matrix of the sum between the two matrices.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the number of rows and columns is different in 
        /// this Matrix and the other Matrix. 
        /// </exception>
        public Matrix<T> Add(Matrix<T> toAdd, Func<T, T, T> sumFunction) {
            if (this.Rows != toAdd.Rows) {
                throw new InvalidOperationException("The number of rows between the two matrices is not equal.");
            }

            if (this.Columns != toAdd.Columns) {
                throw new InvalidOperationException("The number of columns between the two matrices is not equal.");
            }

            Matrix<T> result = new Matrix<T>(Rows, Columns);

            for (int i = 0; i < Rows; i++) {
                for (int j = 0; j < Columns; j++) {
                    result[i, j] = sumFunction(this[i, j], toAdd[i, j]);
                }
            }

            return result;
        }

        /// <summary>
        /// A method that sums asynchronously this Matrix to another.
        /// </summary>
        /// <param name="toAdd">
        /// The Matrix to add to this Matrix instance.
        /// </param>
        /// <returns>
        /// The resulting Matrix of the sum between the two matrices.
        /// </returns>
        /// <remarks>
        /// This method works for the standard .NET numeric types.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the number of rows and columns is different in 
        /// this Matrix and the other Matrix. 
        /// </exception>
        public Task<Matrix<T>> AddAsync(Matrix<T> toAdd) {
            return Task.Run(() => this.Add(toAdd));
        }

        /// <summary>
        /// A method that sums asynchrounously this Matrix to another.
        /// </summary>
        /// <param name="toAdd">
        /// The Matrix to add to this Matrix instance.
        /// </param>
        /// <param name="sumFunction">
        /// The function used to sum two elements of the matrices.
        /// </param>
        /// <returns>
        /// The resulting Matrix of the sum between the two matrices.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the number of rows and columns is different in 
        /// this Matrix and the other Matrix. 
        /// </exception>
        public Task<Matrix<T>> AddAsync(Matrix<T> toAdd, Func<T, T, T> sumFunction) {
            return Task.Run(() => this.Add(toAdd, sumFunction));
        }
        #endregion

        #region multiplication methods
        /// <summary>
        /// A method that multiplies this Matrix to another.
        /// </summary>
        /// <param name="toAdd">
        /// The Matrix to multiply to this Matrix instance.
        /// </param>
        /// <returns>
        /// The resulting Matrix of the multiplication between the two matrices.
        /// </returns>
        /// <remarks>
        /// This method works for the standard .NET numeric types.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the number of rows in the multiplicator Matrix 
        /// and the number of columns in this Matrix are different.
        /// </exception>
        public Matrix<T> Multiply(Matrix<T> multiplicator) {
            return Multiply(
                multiplicator,
                Multiplication == null ? Utilities.Multiplication<T>() : Multiplication,
                Sum == null ? Utilities.Sum<T>() : Sum,
                SumIdentity == null ? Utilities.SumId<T>() : SumIdentity);
        }

        /// <summary>
        /// A method that multiplies this Matrix to another.
        /// </summary>
        /// <param name="toAdd">
        /// The Matrix to multiply to this Matrix instance.
        /// </param>
        /// <param name="multiplicationFunction">
        /// The function used for the multiplication of the Matrix's elements.
        /// </param>
        /// <param name="sumFunction">
        /// The function used for the sum of the Matrix's elements.
        /// </param>
        /// <param name="sumId">
        /// The function used to get the sum id for the Matrix's elements.
        /// </param>
        /// <returns>
        /// The resulting Matrix of the multiplication between the two matrices.
        /// </returns>
        /// <remarks>
        /// This method works for the standard .NET numeric types.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the number of rows in the multiplicator Matrix 
        /// and the number of columns in this Matrix are different.
        /// </exception>
        public Matrix<T> Multiply(Matrix<T> multiplicator, 
                                Func<T, T, T> multiplicationFunction, 
                                Func<T, T, T> sumFunction, 
                                T sumId) {
            if (this.Columns != multiplicator.Rows) {
                throw new InvalidOperationException(string.Format(
                    "{0} {1}",
                    "The number of columns in this Matrix is different from the number of rows in the multiplicator Matrix.",
                    "They must be equal."));
            }

            Matrix<T> result = new Matrix<T>(this.Rows, multiplicator.Columns);

            for (int i = 0; i < this.Rows; i++) {
                for (int j = 0; j < multiplicator.Columns; j++) {
                    result[i, j] = Multiply(
                        row: this.GetRowNoLock(i),
                        column: multiplicator.GetColumnNoLock(j),
                        multiply: multiplicationFunction,
                        add: sumFunction,
                        sumId: sumId);
                }
            }

            return result;
        }

        /// <summary>
        /// A method that asynchronously multiplies this Matrix to another.
        /// </summary>
        /// <param name="toAdd">
        /// The Matrix to multiply to this Matrix instance.
        /// </param>
        /// <returns>
        /// The resulting Matrix of the multiplication between the two matrices.
        /// </returns>
        /// <remarks>
        /// This method works for the standard .NET numeric types.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the number of rows in the multiplicator Matrix 
        /// and the number of columns in this Matrix are different.
        /// </exception>
        public Task<Matrix<T>> MultiplyAsync(Matrix<T> multiplicator) {
            return Task.Run(() => Multiply(multiplicator));
        }

        /// <summary>
        /// A method that multiplies this Matrix to another.
        /// </summary>
        /// <param name="toAdd">
        /// The Matrix to multiply to this Matrix instance.
        /// </param>
        /// <param name="multiplicationFunction">
        /// The function used for the multiplication of the Matrix's elements.
        /// </param>
        /// <param name="sumFunction">
        /// The function used for the sum of the Matrix's elements.
        /// </param>
        /// <param name="getSumId">
        /// The function used to get the sum id for the Matrix's elements.
        /// </param>
        /// <returns>
        /// The resulting Matrix of the multiplication between the two matrices.
        /// </returns>
        /// <remarks>
        /// This method works for the standard .NET numeric types.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the number of rows in the multiplicator Matrix 
        /// and the number of columns in this Matrix are different.
        /// </exception>
        public Task<Matrix<T>> MultiplyAsync(Matrix<T> multiplicator, 
                                            Func<T, T, T> multiplicationFunction, 
                                            Func<T, T, T> sumFunction, 
                                            T sumId) {
            return Task.Run(() => Multiply(multiplicator, multiplicationFunction, sumFunction, sumId));
        }

        /// <summary>
        /// A method that multiplies this Matrix to a scalar.
        /// </summary>
        /// <param name="multiplicator">
        /// The scalar with which this Matrix must be multiplied.
        /// </param>
        /// <returns>
        /// The Matrix resulting from the multiplication operation.
        /// </returns>
        /// <remarks>
        /// This method works for the .NET's standard numeric types.
        /// </remarks>
        public Matrix<T> Multiply(T multiplicator) {
            return Multiply(
                multiplicator, 
                Multiplication == null ? Utilities.Multiplication<T>() : Multiplication);
        }

        /// <summary>
        /// A method that multiplies this Matrix to a scalar.
        /// </summary>
        /// <param name="multiplicator">
        /// The scalar with which this Matrix must be multiplied.
        /// </param>
        /// <param name="multiplicationFunction">
        /// The function used for the multiplication of the elements.
        /// </param>
        /// <returns>
        /// The Matrix resulting from the multiplication operation.
        /// </returns>
        public Matrix<T> Multiply(T multiplicator, Func<T, T, T> multiplicationFunction) {
            Matrix<T> result = new Matrix<T>(Rows, Columns);

            for (int i = 0; i < Rows; i++) {
                for (int j = 0; j < Columns; j++) {
                    result[i, j] = multiplicationFunction(this[i, j], multiplicator);
                }
            }

            return result;
        }

        /// <summary>
        /// A method that asynchronously multiplies this Matrix to a scalar.
        /// </summary>
        /// <param name="multiplicator">
        /// The scalar with which this Matrix must be multiplied.
        /// </param>
        /// <returns>
        /// The Matrix resulting from the multiplication operation.
        /// </returns>
        /// <remarks>
        /// This method works for the .NET's standard numeric types.
        /// </remarks>
        public Task<Matrix<T>> MultiplyAsync(T multiplicator) {
            return Task.Run(() => Multiply(multiplicator));
        }

        /// <summary>
        /// A method that asynchronously multiplies this Matrix to a scalar.
        /// </summary>
        /// <param name="multiplicator">
        /// The scalar with which this Matrix must be multiplied.
        /// </param>
        /// <param name="multiplicationFunction">
        /// The function used for the multiplication of the elements.
        /// </param>
        /// <returns>
        /// The Matrix resulting from the multiplication operation.
        /// </returns>
        public Task<Matrix<T>> MultiplyAsync(T multiplicator, Func<T, T, T> multiplicationFunction) {
            return Task.Run(() => Multiply(multiplicator, multiplicationFunction));
        }

        /// <summary>
        /// A method that multiplies a Matrix's row with another Matrix's column.
        /// </summary>
        /// <param name="row">
        /// The row to multiply to the specified column.
        /// </param>
        /// <param name="column">
        /// The column to multiply to the specified row.
        /// </param>
        /// <returns>
        /// The result of the multiplication.
        /// </returns>
        private static T Multiply(T[] row, T[] column, Func<T, T, T> multiply, Func<T, T, T> add, T sumId) {
            T result = sumId;

            for (int i = 0; i < row.Length; i++) {
                result = add(result, multiply(row[i], column[i]));
            }

            return result;
        }
        #endregion

        #region transpose
        /// <summary>
        /// A method that builds the transposed Matrix of this Matrix.
        /// </summary>
        /// <returns>
        /// The transposed Matrix of this Matrix instance.
        /// </returns>
        public Matrix<T> Transpose() {
            Matrix<T> result = new Matrix<T>(rows: Columns, columns: Rows);

            for (int i = 0; i < Rows; i++) {
                for (int j = 0; j < Columns; j++) {
                    result[j, i] = this[i, j];
                }
            }

            return result;
        }

        /// <summary>
        /// A method that asynchronously builds the transposed Matrix 
        /// of this Matrix.
        /// </summary>
        /// <returns>
        /// The transposed Matrix of this Matrix instance.
        /// </returns>
        public Task<Matrix<T>> TransposeAsync() {
            return Task.Run(() => this.Transpose());
        }
        #endregion

        #region get row
        /// <summary>
        /// Get the row with the specified index.
        /// </summary>
        /// <param name="index">
        /// The index of the desired row.
        /// </param>
        /// <returns>
        /// The row with the specified index.
        /// </returns>
        /// <remarks>
        /// The index is 0-based.
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if the specified index is not in [0, Rows).
        /// </exception>
        public T[] GetRow(int index) {
            if (index < 0) {
                throw new ArgumentOutOfRangeException("index", "The indicated index cannot be smaller than 0.");
            }

            if (index >= Rows) {
                throw new ArgumentOutOfRangeException("index", string.Format("The indicated index cannot be greater than {0}.", Rows - 1));
            }

            T[] result = new T[Columns];

            for (int i = 0; i < Columns; i++) {
                result[i] = this[index, i];
            }

            return result;
        }

        /// <summary>
        /// Get the row with the specified index asynchronously.
        /// </summary>
        /// <param name="index">
        /// The index of the desired row.
        /// </param>
        /// <returns>
        /// The row with the specified index.
        /// </returns>
        /// <remarks>
        /// The index is 0-based.
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if the specified index is not in [0, Rows).
        /// </exception>
        public Task<T[]> GetRowAsync(int index) {
            return Task.Run(() => GetRow(index));
        }

        /// <summary>
        /// A method used to get the row in the specified index without locking it.
        /// </summary>
        /// <param name="index">
        /// The 0-based index of the row.
        /// </param>
        /// <returns>
        /// The row in the specified index.
        /// </returns>
        /// <remarks>
        /// Possible subject of change.
        /// </remarks>
        private T[] GetRowNoLock(int index) {
            T[] result = new T[Columns];

            for (int i = 0; i < Columns; i++) {
                result[i] = elements[index, i];
            }

            return result;
        }
        #endregion

        #region get column
        /// <summary>
        /// Get the column with the specified index.
        /// </summary>
        /// <param name="index">
        /// The index of the desired column.
        /// </param>
        /// <returns>
        /// The column with the specified index.
        /// </returns>
        /// <remarks>
        /// The index is 0-based.
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if the specified index is not in [0, Columns).
        /// </exception>
        public T[] GetColumn(int index) {
            if (index < 0) {
                throw new ArgumentOutOfRangeException("index", "The indicated index cannot be smaller than 0.");
            }

            if (index >= Columns) {
                throw new ArgumentOutOfRangeException("index", string.Format("The indicated index cannot be greater than {0}.", Rows - 1));
            }

            T[] result = new T[Rows];

            for (int i = 0; i < Rows; i++) {
                result[i] = this[i, index];
            }

            return result;
        }

        /// <summary>
        /// Get the column with the specified index asynchronously.
        /// </summary>
        /// <param name="index">
        /// The index of the desired column.
        /// </param>
        /// <returns>
        /// The column with the specified index.
        /// </returns>
        /// <remarks>
        /// The index is 0-based.
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if the specified index is not in [0, Columns).
        /// </exception>
        public Task<T[]> GetColumnAsync(int index) {
            return Task.Run(() => GetColumn(index));
        }

        /// <summary>
        /// A method used to get the column in the specified index without locking it.
        /// </summary>
        /// <param name="index">
        /// The 0-based index of the column.
        /// </param>
        /// <returns>
        /// The column in the specified index.
        /// </returns>
        /// <remarks>
        /// Possible subject of change.
        /// </remarks>
        private T[] GetColumnNoLock(int index) {
            T[] result = new T[Rows];

            for (int i = 0; i < Rows; i++) {
                result[i] = elements[i, index];
            }

            return result;
        }
        #endregion

        #region get trace
        /// <summary>
        /// A method that calculates the trace of this Matrix.
        /// </summary>
        /// <returns>
        /// The trace of this square Matrix.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// If this Matrix is not square.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard numeric types.
        /// </remarks>
        public T GetTrace() {
            return GetTrace(Utilities.Sum<T>(), Utilities.SumId<T>());
        }

        /// <summary>
        /// A method that calculates the trace of this Matrix.
        /// </summary>
        /// <param name="elementSumFunction">
        /// The function used to sum the elements of the Matrix.
        /// </param>
        /// <param name="sumId">
        /// The function used to get the id element of the sum 
        /// operation.
        /// </param>
        /// <returns>
        /// The trace of this square Matrix.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// If this Matrix is not square.
        /// </exception>
        public T GetTrace(Func<T, T, T> elementSumFunction, 
                            T sumId) {
            if (!IsSquare) {
                throw new InvalidOperationException("This operation is possible only on square matrices.");
            }

            T result = sumId;

            for (int i = 0; i < Rows; i++) {
                result = elementSumFunction(result, this[i, i]);
            }

            return result;
        }

        /// <summary>
        /// A method that asynchronously calculates the trace of this Matrix.
        /// </summary>
        /// <returns>
        /// The trace of this square Matrix.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// If this Matrix is not square.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard numeric types.
        /// </remarks>
        public Task<T> GetTraceAsync() {
            return Task.Run(() => GetTrace());
        }

        /// <summary>
        /// A method that asynchronously calculates the trace of 
        /// this Matrix.
        /// </summary>
        /// <param name="elementSumFunction">
        /// The function used to sum the elements of the Matrix.
        /// </param>
        /// <param name="sumId">
        /// The function used to get the id element of the sum 
        /// operation.
        /// </param>
        /// <returns>
        /// The trace of this square Matrix.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// If this Matrix is not square.
        /// </exception>
        public Task<T> GetTraceAsync(Func<T, T, T> elementSumFunction, 
                                    T sumId) {
            return Task.Run(() => GetTrace(elementSumFunction, sumId));
        }
        #endregion

        #region get determinant
        /// <summary>
        /// A method that calculates and returns the 
        /// determinant of this square Matrix.
        /// </summary>
        /// <returns>
        /// The determinant of this square Matrix.
        /// </returns>
        /// <remarks>
        /// This method works for .NET's standard numeric 
        /// types.
        /// </remarks>
        public T GetDeterminant() {
            return GetDeterminant(
                elementMultiplicationFunction: Multiplication == null ? Utilities.Multiplication<T>() : Multiplication,
                elementSumFunction: Sum == null ? Utilities.Sum<T>() : Sum,
                elementSubtractionFunction: Subtraction == null ? Utilities.Subtraction<T>() : Subtraction,
                multiplicationId: MultiplicationIdentity == null ? Utilities.MultiplicationId<T>() : MultiplicationIdentity,
                sumId: SumIdentity == null ? Utilities.SumId<T>() : SumIdentity);
        }

        /// <summary>
        /// A method that calculates and returns the 
        /// determinant of this square Matrix.
        /// </summary>
        /// <param name="elementMultiplicationFunction">
        /// The function used for the multiplication of the
        /// elements of this Matrix.
        /// </param>
        /// <param name="elementSumFunction">
        /// The function used for the sum of the elements of
        /// this Matrix.
        /// </param>
        /// <param name="elementSubtractionFunction">
        /// The function used for the subtraction of the 
        /// elements of this Matrix.
        /// </param>
        /// <param name="multiplicationId">
        /// The function used to get the multiplication id 
        /// for the elements of this Matrix.
        /// </param>
        /// <param name="sumId">
        /// The function used to get the sum id for the 
        /// elements of this Matrix.
        /// </param>
        /// <returns>
        /// The determinant of this square Matrix.
        /// </returns>
        public T GetDeterminant(Func<T, T, T> elementMultiplicationFunction,
                                Func<T, T, T> elementSumFunction,
                                Func<T, T, T> elementSubtractionFunction,
                                T multiplicationId,
                                T sumId) {
            if (!IsSquare) {
                throw new InvalidOperationException("This operation is possible only on square matrices.");
            }

            T   topDownResult,
                bottomUpResult,
                tempResult;

            lock (thisLock) {
                topDownResult = sumId;
                bottomUpResult = sumId;
                tempResult = multiplicationId;

                // let's calculate the sum of the top-down diagonals
                for (int i = 0; i < Rows; i++) {
                    tempResult = multiplicationId;

                    for (int j = 0; j < Rows; j++) {
                        tempResult = elementMultiplicationFunction(tempResult, this[j, (i + j) % Rows]);
                    }

                    topDownResult = elementSumFunction(topDownResult, tempResult);
                }

                // now let's calculate the sum of the bottom-up diagonals
                for (int i = 0; i < Rows; i++) {
                    tempResult = multiplicationId;

                    for (int j = 0; j < Rows; j++) {
                        tempResult = elementMultiplicationFunction(tempResult, this[(Rows - j - 1), (i + j) % Rows]);
                    }

                    bottomUpResult = elementSumFunction(bottomUpResult, tempResult);
                }
            }

            return elementSumFunction(topDownResult, bottomUpResult);
        }

        /// <summary>
        /// A method that asynchronously calculates and returns the 
        /// determinant of this square Matrix.
        /// </summary>
        /// <returns>
        /// The determinant of this square Matrix.
        /// </returns>
        /// <remarks>
        /// This method works for .NET's standard numeric 
        /// types.
        /// </remarks>
        public Task<T> GetDeterminantAsync() {
            return Task.Run(() => GetDeterminant());
        }

        /// <summary>
        /// A method that asynchronously calculates and returns the 
        /// determinant of this square Matrix.
        /// </summary>
        /// <param name="elementMultiplicationFunction">
        /// The function used for the multiplication of the
        /// elements of this Matrix.
        /// </param>
        /// <param name="elementSumFunction">
        /// The function used for the sum of the elements of
        /// this Matrix.
        /// </param>
        /// <param name="elementSubtractionFunction">
        /// The function used for the subtraction of the 
        /// elements of this Matrix.
        /// </param>
        /// <param name="multiplicationId">
        /// The function used to get the multiplication id 
        /// for the elements of this Matrix.
        /// </param>
        /// <param name="sumId">
        /// The function used to get the sum id for the 
        /// elements of this Matrix.
        /// </param>
        /// <returns>
        /// The determinant of this square Matrix.
        /// </returns>
        public Task<T> GetDeterminantAsync(Func<T, T, T> elementMultiplicationFunction,
                                Func<T, T, T> elementSumFunction,
                                Func<T, T, T> elementSubtractionFunction,
                                T multiplicationId,
                                T sumId) {
            return Task.Run(() => GetDeterminant(
                elementMultiplicationFunction,
                elementSumFunction,
                elementSubtractionFunction,
                multiplicationId,
                sumId));
        }
        #endregion

        #region equals
        /// <summary>
        /// A method that checks if a given object is equal to 
        /// this Matrix.
        /// </summary>
        /// <param name="obj">
        /// The object to check for equality with this Matrix.
        /// </param>
        /// <returns>
        /// TRUE if obj is equal to this Matrix, FALSE otherwise.
        /// </returns>
        public override bool Equals(object obj) {
            return (obj is Matrix<T>) && this.Equals((Matrix<T>)obj);
        }

        /// <summary>
        /// A method that checks if this Matrix is equal to another Matrix.
        /// </summary>
        /// <param name="other">
        /// The other Matrix to check for equality.
        /// </param>
        /// <returns>
        /// TRUE if the two matrices are equal, FALSE otherwise.
        /// </returns>
        public bool Equals(Matrix<T> other) {
            bool result = (Rows == other.Rows) && (Columns == other.Columns);

            for (int i = 0; result && i < Rows; i++) {
                for (int j = 0; result && j < Columns; j++) {
                    result = result && this[i, j].Equals(other[i, j]);
                }
            }

            return result;
        }

        /// <summary>
        /// A method that asynchronously checks if this Matrix 
        /// is equal to the indicated object.
        /// </summary>
        /// <param name="obj">
        /// The object to check for equality.
        /// </param>
        /// <returns>
        /// TRUE if the two matrices are equal, FALSE otherwise.
        /// </returns>
        public Task<bool> EqualsAsync(object obj) {
            return Task.Run(() => this.Equals(obj));
        }

        /// <summary>
        /// A method that asynchronously checks if this Matrix 
        /// is equal to another Matrix.
        /// </summary>
        /// <param name="other">
        /// The other Matrix to check for equality.
        /// </param>
        /// <returns>
        /// TRUE if the two matrices are equal, FALSE otherwise.
        /// </returns>
        public Task<bool> EqualsAsync(Matrix<T> other) {
            return Task.Run(() => this.Equals(other));
        }
        #endregion

        #region to string
        /// <summary>
        /// Get the string representation of the Matrix.
        /// </summary>
        /// <returns>
        /// The string representation of the Matrix.
        /// </returns>
        public override string ToString() {
            string result = "";

            for (int i = 0; i < Rows; i++) {
                for (int j = 0; j < Columns; j++) {
                    result += string.Format("{0}\t", this[i, j]);
                }

                result += "\n";
            }

            return result;
        }

        /// <summary>
        /// Asynchronously get the string representation 
        /// of the Matrix.
        /// </summary>
        /// <returns>
        /// The string representation of the Matrix.
        /// </returns>
        public Task<string> ToStringAsync() {
            return Task.Run(() => this.ToString());
        }
        #endregion

        #region get identity
        /// <summary>
        /// A method that gets the Identity Matrix 
        /// of a given size for the multiplication
        /// operation.
        /// </summary>
        /// <param name="size">
        /// The desired size of the Identity Matrix.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified size is smaller than 1.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard 
        /// numeric types.
        /// </remarks>
        public static Matrix<T> GetMultiplicationIdentity(int size = 2) {
            return GetMultiplicationIdentity(
                size,
                Utilities.MultiplicationId<T>(),
                Utilities.SumId<T>());
        }

        /// <summary>
        /// A method that gets the Identity Matrix 
        /// of a given size.
        /// </summary>
        /// <param name="size">
        /// The desired size of the Identity Matrix.
        /// </param>
        /// <param name="elementMultiplicationId">
        /// The function that returns the element's id for the 
        /// multiplication operation.
        /// </param>
        /// <param name="elementSumId">
        /// The function that returns the element's id for the 
        /// addition operation.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified size is smaller than 1.
        /// </exception>
        public static Matrix<T> GetMultiplicationIdentity(
                        int size,
                        T elementMultiplicationId,
                        T elementSumId) {
            if (size < 1) {
                throw new ArgumentOutOfRangeException("size", "The size cannot be smaller than 1.");
            }

            Matrix<T> result = new Matrix<T>(size);
            T multiplicationId = elementMultiplicationId;
            T sumId = elementSumId;

            for (int i = 0; i < size; i++) {
                for (int j = 0; j < size; j++) {
                    result[i, j] = (i == j) ? multiplicationId : sumId;
                }
            }

            return result;
        }

        /// <summary>
        /// A method that gets the square Identity Matrix 
        /// of a given size for the addition operation.
        /// </summary>
        /// <param name="size">
        /// The desired size of the square Identity Matrix.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified sizes are smaller than 1.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard numeric 
        /// types.
        /// </remarks>
        public static Matrix<T> GetSumIdentity(int size = 2) {
            return GetSumIdentity(size, size);
        }

        /// <summary>
        /// A method that gets the Identity Matrix 
        /// of a given size for the addition operation.
        /// </summary>
        /// <param name="rows">
        /// The desired rowsize of the Identity Matrix.
        /// </param>
        /// <param name="columns">
        /// The desired column size of the Identity Matrix.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified sizes are smaller than 1.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard numeric 
        /// types.
        /// </remarks>
        public static Matrix<T> GetSumIdentity(int rows, int columns) {
            return GetSumIdentity(rows, columns, Utilities.SumId<T>());
        }

        /// <summary>
        /// A method that gets the Identity Matrix 
        /// of a given size for the addition operation.
        /// </summary>
        /// <param name="rows">
        /// The desired rowsize of the Identity Matrix.
        /// </param>
        /// <param name="columns">
        /// The desired column size of the Identity Matrix.
        /// </param>
        /// <param name="elementSumId">
        /// The function that gets the id of the Matrix's 
        /// element's for the addition operation.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified sizes are smaller than 1.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard numeric 
        /// types.
        /// </remarks>
        public static Matrix<T> GetSumIdentity(
                                    int rows,
                                    int columns,
                                    T elementSumId) {
            if (rows < 1) {
                throw new ArgumentOutOfRangeException("rows", "The number of rows must be greater than 0.");
            }

            if (columns < 1) {
                throw new ArgumentOutOfRangeException("columns", "The number of columns must be greater than 0.");
            }

            Matrix<T> result = new Matrix<T>(rows, columns);
            T sumId = elementSumId;

            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < columns; j++) {
                    result[i, j] = sumId;
                }
            }

            return result;
        }

        /// <summary>
        /// A method that asynchronously gets the Identity Matrix 
        /// of a given size for the multiplication operation.
        /// </summary>
        /// <param name="size">
        /// The desired size of the Identity Matrix.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified size is smaller than 1.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard 
        /// numeric types.
        /// </remarks>
        public static Task<Matrix<T>> GetMultiplicationIdentityAsync(int size = 2) {
            return Task.Run(() => GetMultiplicationIdentity(size));
        }

        /// <summary>
        /// A method that asynchronously gets the Identity Matrix 
        /// of a given size.
        /// </summary>
        /// <param name="size">
        /// The desired size of the Identity Matrix.
        /// </param>
        /// <param name="elementMultiplicationId">
        /// The function that returns the element's id for the 
        /// multiplication operation.
        /// </param>
        /// <param name="elementSumId">
        /// The function that returns the element's id for the 
        /// addition operation.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified size is smaller than 1.
        /// </exception>
        public static Task<Matrix<T>> GetMultiplicationIdentityAsync(
                    int size,
                    T elementMultiplicationId,
                    T elementSumId) {
            return Task.Run(() => GetMultiplicationIdentity(size, elementMultiplicationId, elementSumId));
        }

        /// <summary>
        /// A method that asynchronously gets the Identity Matrix 
        /// of a given size for the addition operation.
        /// </summary>
        /// <param name="rows">
        /// The desired rowsize of the Identity Matrix.
        /// </param>
        /// <param name="columns">
        /// The desired column size of the Identity Matrix.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified sizes are smaller than 1.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard numeric 
        /// types.
        /// </remarks>
        public static Task<Matrix<T>> GetSumIdentityAsync(int rows, int columns) {
            return Task.Run(() => GetSumIdentity(rows, columns));
        }

        /// <summary>
        /// A method that asynchronously gets the Identity Matrix 
        /// of a given size for the addition operation.
        /// </summary>
        /// <param name="rows">
        /// The desired rowsize of the Identity Matrix.
        /// </param>
        /// <param name="columns">
        /// The desired column size of the Identity Matrix.
        /// </param>
        /// <param name="elementSumId">
        /// The function that gets the id of the Matrix's 
        /// element's for the addition operation.
        /// </param>
        /// <returns>
        /// The Identity Matrix of a given size.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the specified sizes are smaller than 1.
        /// </exception>
        /// <remarks>
        /// This method works for .NET's standard numeric 
        /// types.
        /// </remarks>
        public static Task<Matrix<T>> GetSumIdentityAsync(
                                    int rows, 
                                    int columns, 
                                    T elementSumId) {
            return Task.Run(() => GetSumIdentity(rows, columns, elementSumId));
        }
        #endregion

        #region get hash code
        /// <summary>
        /// Get the hash code for this Matrix.
        /// </summary>
        /// <returns>
        /// The hash code for this Matrix instance.
        /// </returns>
        public override int GetHashCode() {
            int hashCode = 0;

            for (int i = 0; i < Rows; i++) {
                for (int j = 0; j < Columns; j++) {
                    hashCode ^= this[i, j].GetHashCode();
                }
            }

            return hashCode;
        }

        /// <summary>
        /// Asynchronously get the hash code for this Matrix.
        /// </summary>
        /// <returns>
        /// The hash code for this Matrix instance.
        /// </returns>
        public Task<int> GetHashCodeAsync() {
            return Task.Run(() => GetHashCode());
        }
        #endregion

        #region copy matrix
        /// <summary>
        /// Copy this matrix to a destination Matrix of the 
        /// specified size.
        /// </summary>
        /// <param name="dimension">
        /// The dimension of the square matrix resulting 
        /// from the resizing.
        /// </param>
        /// <returns>
        /// The square matrix resulting from the resizing.
        /// </returns>
        public Matrix<T> Copy(int dimension = 2)
        {
            return Copy(dimension, dimension);
        }

        /// <summary>
        /// Copy this matrix to a destination Matrix of the 
        /// specified size.
        /// </summary>
        /// <param name="rows">
        /// The desired number of rows.
        /// </param>
        /// <param name="cols">
        /// The desired number of columns.
        /// </param>
        /// <returns>
        /// The square matrix resulting from the resizing.
        /// </returns>
        public Matrix<T> Copy(int rows, int cols)
        {
            Matrix<T> result = new Matrix<T>(rows, cols);

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    result[i, j] = i < Rows && j < Columns
                                    ? this[i, j]
                                    : default(T);
                }
            }

            return result;
        }

        /// <summary>
        /// Copy this matrix to a destination Matrix of the 
        /// specified size.
        /// </summary>
        /// <param name="dimension">
        /// The dimension of the square matrix resulting 
        /// from the resizing.
        /// </param>
        /// <returns>
        /// The square matrix resulting from the resizing.
        /// </returns>
        public Task<Matrix<T>> CopyAsync(int dimension)
        {
            return Task.Run(() => Copy(dimension));
        }

        /// <summary>
        /// Copy this matrix to a destination Matrix of the 
        /// specified size.
        /// </summary>
        /// <param name="rows">
        /// The desired number of rows.
        /// </param>
        /// <param name="cols">
        /// The desired number of columns.
        /// </param>
        /// <returns>
        /// The square matrix resulting from the resizing.
        /// </returns>
        public Task<Matrix<T>> CopyAsync(int rows, int cols)
        {
            return Task.Run(() => Copy(rows, cols));
        }
        #endregion

        #region clone
        /// <summary>
        /// Clone the structure and data of this Matrix.
        /// </summary>
        /// <returns>
        /// A clone of this Matrix.
        /// </returns>
        public Matrix<T> Clone()
        {
            return Copy(Rows, Columns);
        }

        /// <summary>
        /// Clone the structure and data of this Matrix.
        /// </summary>
        /// <returns>
        /// A clone of this Matrix.
        /// </returns>
        public Task<Matrix<T>> CloneAsync()
        {
            return CopyAsync(Rows, Columns);
        }
        #endregion
        #endregion

        #region operators
        /// <summary>
        /// Get the Matrix result of the sum between two other matrices.
        /// </summary>
        /// <param name="left">
        /// The Matrix on the left side of the operator.
        /// </param>
        /// <param name="right">
        /// The Matrix on the right side of the operator.
        /// </param>
        /// <returns>
        /// The Matrix result of the sum between the two specified matrices.
        /// </returns>
        /// <remarks>
        /// This operator works correctly for the .NET's standard
        /// numeric types.
        /// </remarks>
        public static Matrix<T> operator +(Matrix<T> left, Matrix<T> right) {
            return left.Add(right);
        }

        /// <summary>
        /// Get the Matrix result of the multiplication between 
        /// two other matrices.
        /// </summary>
        /// <param name="left">
        /// The Matrix on the left side of the operator.
        /// </param>
        /// <param name="right">
        /// The Matrix on the right side of the operator.
        /// </param>
        /// <returns>
        /// The Matrix result of the multiplication between the 
        /// two specified matrices.
        /// </returns>
        /// <remarks>
        /// This operator works correctly for the .NET's standard
        /// numeric types.
        /// </remarks>
        public static Matrix<T> operator *(Matrix<T> left, Matrix<T> right) {
            return left.Multiply(right);
        }

        /// <summary>
        /// Get the Matrix result of the multiplication between 
        /// a Matrix and a scalar value.
        /// </summary>
        /// <param name="left">
        /// The Matrix on the left side of the operator.
        /// </param>
        /// <param name="right">
        /// The scalar value on the right side of the operator.
        /// </param>
        /// <returns>
        /// The Matrix result of the multiplication between the 
        /// Matrix and the scalar.
        /// </returns>
        /// <remarks>
        /// This operator works correctly for the .NET's standard
        /// numeric types.
        /// </remarks>
        public static Matrix<T> operator *(Matrix<T> left, T right) {
            return left.Multiply(right);
        }

        /// <summary>
        /// Get the Matrix result of the multiplication between 
        /// a scalar value and a Matrix.
        /// </summary>
        /// <param name="left">
        /// The scalar value on the left side of the operator.
        /// </param>
        /// <param name="right">
        /// The Matrix on the right side of the operator.
        /// </param>
        /// <returns>
        /// The Matrix result of the multiplication between the 
        /// Matrix and the scalar.
        /// </returns>
        /// <remarks>
        /// This operator works correctly for the .NET's standard
        /// numeric types.
        /// </remarks>
        public static Matrix<T> operator *(T left, Matrix<T> right) {
            return right.Multiply(left);
        }

        /// <summary>
        /// Check if two matrices are equal.
        /// </summary>
        /// <param name="left">
        /// The scalar value on the left side of the operator.
        /// </param>
        /// <param name="right">
        /// The Matrix on the right side of the operator.
        /// </param>
        /// <returns>
        /// TRUE if the two matrices are equal, FALSE otherwise.
        /// </returns>
        public static bool operator ==(Matrix<T> left, Matrix<T> right) {
            return left.Equals(right);
        }

        /// <summary>
        /// Check if two matrices are different.
        /// </summary>
        /// <param name="left">
        /// The scalar value on the left side of the operator.
        /// </param>
        /// <param name="right">
        /// The Matrix on the right side of the operator.
        /// </param>
        /// <returns>
        /// TRUE if the two matrices are different, FALSE otherwise.
        /// </returns>
        public static bool operator !=(Matrix<T> left, Matrix<T> right) {
            return !(left.Equals(right));
        }
        #endregion
    }
}
